/**
 * @file can_config.c
 * @author your name (you@domain.com)
 * @brief 使用了CAN1和CAN2，CAN1接收数据绑定RX_FIFO0，CAN2接收数据绑定RX_FIFO1
 * 波特率均时500kb/s，开启了接收中断
 * @version 0.1
 * @date 2022-03-15
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include <stdint.h>
#include "can_config.h"
#include "stm32f4xx_hal.h"
#include "bsp.h"
#include "my_rtt_print_log.h"

// 与主控通信的CAN针脚、时钟配置
// CMD:Command
#define CMD_CAN_TX_Pin          GPIO_PIN_11
#define CMD_CAN_TX_GPIO_Port    GPIOA
#define CMD_CAN_TX_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define CMD_CAN_RX_Pin          GPIO_PIN_12
#define CMD_CAN_RX_GPIO_Port    GPIOA
#define CMD_CAN_RX_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define CMD_CAN_PIN_AF          GPIO_AF9_CAN1

#define CMD_CAN_CLK_ENABLE()    __HAL_RCC_CAN1_CLK_ENABLE()
#define CMD_CAN_CLK_DISABLE()   __HAL_RCC_CAN1_CLK_DISABLE()

// 与电机伺服驱动器通信的CAN针脚、时钟配置
// MC:Moter Control
#define MC_CAN_TX_Pin           GPIO_PIN_6
#define MC_CAN_TX_GPIO_Port     GPIOB
#define MC_CAN_TX_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()

#define MC_CAN_RX_Pin           GPIO_PIN_5
#define MC_CAN_RX_GPIO_Port     GPIOB
#define MC_CAN_RX_CLK_ENABLE()  __HAL_RCC_GPIOB_CLK_ENABLE()

#define MC_CAN_PIN_AF           GPIO_AF9_CAN2

#define MC_CAN_CLK_ENABLE()     __HAL_RCC_CAN2_CLK_ENABLE()
#define MC_CAN_CLK_DISABLE()    __HAL_RCC_CAN2_CLK_DISABLE()

// 与主控通信的CAN接口的发送中断及接收0中断
#define CMD_CAN_TX_IRQ_PRIO 6           // 未启用
#define CMD_CAN_TX_IRQn     CAN1_TX_IRQn

#define CMD_CAN_RX_IRQ_PRIO 7
#define CMD_CAN_RX_IRQn     CAN1_RX0_IRQn

// 与电机驱动器通信的CAN接口的发送中断及接收0中断
#define MC_CAN_TX_IRQ_PRIO  1           // 未启用
#define MC_CAN_TX_IRQn      CAN2_TX_IRQn

#define MC_CAN_RX_IRQ_PRIO  4
#define MC_CAN_RX_IRQn      CAN2_RX1_IRQn

// 配置CAN缺省波特率,500kbit/s
#define DEFAULT_CAN_PRESCALE 6
#define DEFAULT_CAN_SJW CAN_SJW_1TQ
#define DEFAULT_CAN_BS1 CAN_BS1_8TQ
#define DEFAULT_CAN_BS2 CAN_BS2_5TQ


CAN_HandleTypeDef hcanx[CAN_TOTAL_NUM] = 
{
    [CAN1_INDEX] =
    {
        .Instance = CMD_CAN,
        .Init.Prescaler = DEFAULT_CAN_PRESCALE,
        .Init.Mode = CAN_MODE_NORMAL,
        .Init.SyncJumpWidth = DEFAULT_CAN_SJW,
        .Init.TimeSeg1 = DEFAULT_CAN_BS1,
        .Init.TimeSeg2 = DEFAULT_CAN_BS2,
        .Init.TimeTriggeredMode = DISABLE,
        .Init.AutoBusOff = ENABLE,
        .Init.AutoWakeUp = DISABLE,
        .Init.AutoRetransmission = ENABLE,
        .Init.ReceiveFifoLocked = DISABLE,
        .Init.TransmitFifoPriority = DISABLE,
    },
    [CAN2_INDEX] = 
    {
        .Instance = MC_CAN,
        .Init.Prescaler = DEFAULT_CAN_PRESCALE,
        .Init.Mode = CAN_MODE_NORMAL,
        .Init.SyncJumpWidth = DEFAULT_CAN_SJW,
        .Init.TimeSeg1 = DEFAULT_CAN_BS1,
        .Init.TimeSeg2 = DEFAULT_CAN_BS2,
        .Init.TimeTriggeredMode = DISABLE,
        .Init.AutoBusOff = ENABLE,
        .Init.AutoWakeUp = DISABLE,
        .Init.AutoRetransmission = ENABLE,
        .Init.ReceiveFifoLocked = DISABLE,
        .Init.TransmitFifoPriority = DISABLE,
    },
};

static void configCanFilter(CAN_HandleTypeDef *hcan);
static void configFilterMask(CAN_HandleTypeDef *hcan, uint8_t filter_num);
static void canStart(CAN_HandleTypeDef *hcan);

/**
 * @brief Set the Can Bit Rate object
 * 
 * @param bit_rate value range: 125, 250, 500, 1000
 * @param hcan CAN_HandleTypeDef指针类型
 */
void setCanBitRate(uint16_t bit_rate, CAN_HandleTypeDef* hcan)
{
    switch (bit_rate)
    {
    case 500:
        hcan->Init.Prescaler = 6;
        hcan->Init.SyncJumpWidth = CAN_SJW_1TQ;
        hcan->Init.TimeSeg1 = CAN_BS1_8TQ;
        hcan->Init.TimeSeg2 = CAN_BS2_5TQ;
        break;
    case 1000:
        hcan->Init.Prescaler = 3;
        hcan->Init.SyncJumpWidth = CAN_SJW_1TQ;
        hcan->Init.TimeSeg1 = CAN_BS1_8TQ;
        hcan->Init.TimeSeg2 = CAN_BS2_5TQ;
        break;
    case 250:
        hcan->Init.Prescaler = 12;
        hcan->Init.SyncJumpWidth = CAN_SJW_1TQ;
        hcan->Init.TimeSeg1 = CAN_BS1_8TQ;
        hcan->Init.TimeSeg2 = CAN_BS2_5TQ;
        break;
    case 125:
        hcan->Init.Prescaler = 24;
        hcan->Init.SyncJumpWidth = CAN_SJW_1TQ;
        hcan->Init.TimeSeg1 = CAN_BS1_8TQ;
        hcan->Init.TimeSeg2 = CAN_BS2_5TQ;
        break;
    default:
        break;
    }
}


/**
 * @brief 配置CAN总线时钟，GPIO，中断等，
 * 由canxInit()函数中的HAL_CAN_Init()函数调用
 * 
 * @param canHandle CAN_HandleTypeDef指针类型
 */
void HAL_CAN_MspInit(CAN_HandleTypeDef *canHandle)
{

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (canHandle->Instance == CMD_CAN)
    {
        /* CAN1 clock enable */
        CMD_CAN_CLK_ENABLE();

        CMD_CAN_TX_CLK_ENABLE();
        CMD_CAN_RX_CLK_ENABLE();

        /**CAN1 GPIO Configuration
        PB8     ------> CAN1_RX
        PB9     ------> CAN1_TX
        */
        GPIO_InitStruct.Pin = CMD_CAN_TX_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        GPIO_InitStruct.Alternate = CMD_CAN_PIN_AF;
        HAL_GPIO_Init(CMD_CAN_TX_GPIO_Port, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = CMD_CAN_RX_Pin;
        HAL_GPIO_Init(CMD_CAN_RX_GPIO_Port, &GPIO_InitStruct);

        /* CAN1 interrupt Init */
        // HAL_NVIC_SetPriority(CMD_CAN_TX_IRQn, CMD_CAN_TX_IRQ_PRIO, 0);
        // HAL_NVIC_EnableIRQ(CMD_CAN_TX_IRQn);
        HAL_NVIC_SetPriority(CMD_CAN_RX_IRQn, CMD_CAN_RX_IRQ_PRIO, 0);
        HAL_NVIC_EnableIRQ(CMD_CAN_RX_IRQn);
    }
    else if (canHandle->Instance == MC_CAN)
    {
        /* CAN2 clock enable */
        CMD_CAN_CLK_ENABLE();		
        MC_CAN_CLK_ENABLE();

        MC_CAN_TX_CLK_ENABLE();
        MC_CAN_RX_CLK_ENABLE();
        /**CAN2 GPIO Configuration
        PB5     ------> CAN2_RX
        PB6     ------> CAN2_TX
        */
        GPIO_InitStruct.Pin = MC_CAN_TX_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
        GPIO_InitStruct.Alternate = MC_CAN_PIN_AF;
        HAL_GPIO_Init(MC_CAN_TX_GPIO_Port, &GPIO_InitStruct);

        GPIO_InitStruct.Pin = MC_CAN_RX_Pin;
        HAL_GPIO_Init(MC_CAN_RX_GPIO_Port, &GPIO_InitStruct);

        /* CAN2 interrupt Init */
        // HAL_NVIC_SetPriority(MC_CAN_TX_IRQn, MC_CAN_TX_IRQ_PRIO, 0);
        // HAL_NVIC_EnableIRQ(MC_CAN_TX_IRQn);        
        HAL_NVIC_SetPriority(MC_CAN_RX_IRQn, MC_CAN_RX_IRQ_PRIO, 0);
        HAL_NVIC_EnableIRQ(MC_CAN_RX_IRQn);
    }
}

/**
 * @brief 由HAL_CAN_DeInit调用
 * 
 * @param canHandle CAN_HandleTypeDef指针类型
 */
void HAL_CAN_MspDeInit(CAN_HandleTypeDef *canHandle)
{
    if (canHandle->Instance == CMD_CAN)
    {
        /* Peripheral clock disable */
        CMD_CAN_CLK_DISABLE();

        HAL_GPIO_DeInit(CMD_CAN_TX_GPIO_Port, CMD_CAN_TX_Pin);
        HAL_GPIO_DeInit(CMD_CAN_RX_GPIO_Port, CMD_CAN_RX_Pin);

        /* CAN1 interrupt Deinit */
        HAL_NVIC_DisableIRQ(CMD_CAN_TX_IRQn);
        HAL_NVIC_DisableIRQ(CMD_CAN_RX_IRQn);
    }
    else if (canHandle->Instance == MC_CAN)
    {
        /* Peripheral clock disable */
        MC_CAN_CLK_DISABLE();

        HAL_GPIO_DeInit(MC_CAN_TX_GPIO_Port, MC_CAN_TX_Pin);
        HAL_GPIO_DeInit(MC_CAN_RX_GPIO_Port, MC_CAN_RX_Pin);

        /* CAN2 interrupt Deinit */
        HAL_NVIC_DisableIRQ(MC_CAN_TX_IRQn);
        HAL_NVIC_DisableIRQ(MC_CAN_RX_IRQn);
    }
}

/**
 * @brief 配置CAN的滤波器
 * 
 * @param hcan CAN_HandleTypeDef指针类型
 */
static void configCanFilter(CAN_HandleTypeDef *hcan)
{
    const uint8_t CAN1_FILTER_NUM1 = 0;
    const uint8_t CAN2_FILTER_NUM1 = 16;
    if (CMD_CAN == hcan->Instance)
    {
        // TODO:暂时不屏蔽所有CAN帧，后续制定出通信协议时，可屏蔽不需要的CAN帧，减少进中断的次数和时间
        configFilterMask(hcan, CAN1_FILTER_NUM1);
    }
    else if (MC_CAN == hcan->Instance)
    {
        // TODO:暂时不屏蔽所有CAN帧，后续制定出通信协议时，可屏蔽不需要的CAN帧，减少进中断的次数和时间
        configFilterMask(hcan, CAN2_FILTER_NUM1);
    }
}

/**
 * @brief 配置CAN滤波器为MASK形式，不屏蔽CAN帧
 * 
 * @param hcan 
 * @param filter_num 滤波器编号
 */
static void configFilterMask(CAN_HandleTypeDef *hcan, uint8_t filter_num)
{
    const uint32_t CAN2_FILTER_START = 14;

    CAN_FilterTypeDef sFilterConfig;
    sFilterConfig.FilterBank = filter_num; 

    sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
    sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
    sFilterConfig.FilterIdHigh = 0x0000; //
    sFilterConfig.FilterIdLow = 0x0000;  //the bits we care
    sFilterConfig.FilterMaskIdHigh = 0x0000;
    sFilterConfig.FilterMaskIdLow = 0x0000;

    if (CMD_CAN == hcan->Instance)
    {
        sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
        sFilterConfig.SlaveStartFilterBank = CAN2_FILTER_START;
    }
    else if (MC_CAN == hcan->Instance)
    {
        sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO1;
    }
    
    sFilterConfig.FilterActivation = ENABLE;

    if (HAL_CAN_ConfigFilter(hcan, &sFilterConfig) != HAL_OK)
    {
        /* Filter configuration Error */
        //printf("Filter configuration Error \r\n");
    }
}

/**
 * @brief 启动CAN并激活相应中断
 * 
 * @param hcan 
 */ 
static void canStart(CAN_HandleTypeDef *hcan)
{
    // 启动CAN1
    if (HAL_CAN_Start(hcan) != HAL_OK)
    {
        myRttPrintLog(0, "open CAN failed \r\n");
    }

    // 激活CAN接收中断
    if (CMD_CAN == hcan->Instance)
    {
        if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING)!= HAL_OK) // 激活CAN1 FIFO0
        {
            myRttPrintLog(0, "open can1 CAN_IT_RX_FIFO0_MSG_PENDING interrupt failed\r\n");
        }
    }
    else if (MC_CAN == hcan->Instance)
    {
        if (HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO1_MSG_PENDING) != HAL_OK) // 激活CAN2 FIFO1	
        {
            myRttPrintLog(0, "open can2 CAN_IT_RX_FIFO1_MSG_PENDING interrupt failed\r\n");
        }
    }
}


/**
 * @brief 初始化CAN
 * 
 * @param CAN_No CAN序号
 */
void canxInit(uint8 port, uint32 baudrate)
{
    uint8 index = port;
    setCanBitRate(baudrate, &hcanx[index]); // 配置CANx波特率

    /* 复位CAN */
    if (HAL_CAN_DeInit(&hcanx[index]) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }

    /* 初始化CAN */
    if (HAL_CAN_Init(&hcanx[index]) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }
            
    // 配置CAN滤波器
    configCanFilter(&hcanx[index]);

    // 启动CAN并激活相应中断
    canStart(&hcanx[index]);				
}


